package com.region.loadbalancer.http.apache;

import com.region.common.adapter.logging.LoggerAdapter;
import com.region.common.adapter.logging.LoggerAdapterFactory;
import com.region.common.utils.ReflectUtils;
import com.region.loadbalancer.LoadBalancerManager;
import com.region.loadbalancer.group.Server;
import com.region.plugin.Interceptor;
import com.region.plugin.ProceedPoint;
import org.apache.http.client.methods.HttpRequestBase;

import java.lang.reflect.Method;

/**
 * Apache Http Client Interceptor
 *
 * @author liujieyu
 * @date 2023/5/30 20:31
 * @desciption
 */
public class ApacheHttpClientInterceptor implements Interceptor {

    private final LoggerAdapter loggerAdapter = LoggerAdapterFactory.getLogger(getClass());

    private LoadBalancerManager loadBalancerManager;

    public ApacheHttpClientInterceptor(LoadBalancerManager loadBalancerManager) {
        this.loadBalancerManager = loadBalancerManager;
    }

    @Override
    public Object proceed(ProceedPoint proceed) throws Throwable {
        Method method = (Method) ReflectUtils.getFieldValueByObject("method", proceed);
        if ("execute".equals(method.getName())) {
            // Get execute method parameter
            Object[] args = (Object[]) ReflectUtils.getFieldValueByObject("args", proceed);
            String groupName = null;
            HttpRequestBase requestBase = null;
            // Choose server
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof HttpRequestBase) {
                    requestBase = (HttpRequestBase) args[i];
                    groupName = requestBase.getURI().getAuthority();
                    break;
                }
            }

            return this.loadBalancerRequest(requestBase, groupName, proceed);
        } else {
            return proceed.proceed();
        }
    }

    /**
     * Load balancer request http
     *
     * @param requestBase
     * @param groupName
     * @param proceedPoint
     * @return
     */
    private Object loadBalancerRequest(HttpRequestBase requestBase, String groupName, ProceedPoint proceedPoint) {
        Server server = loadBalancerManager.chooseServer(groupName);
        System.out.println(server);
        if (server == null) {
            loggerAdapter.warn("Get load balancing server is null");
            throw new IllegalStateException("Get Load Balancing Service Exception");
        }
        ReflectUtils.setFieldValue(requestBase.getURI(), "host", server.getHost());
        ReflectUtils.setFieldValue(requestBase.getURI(), "port", server.getPort());
        long startTime = System.nanoTime();
        try {
            Object realVal = proceedPoint.proceed();
            // Save request time
            loadBalancerManager.saveServerResponseTime(groupName, server, 1.0 * (System.nanoTime() - startTime) / (1000 * 1000));
            return realVal;
        } catch (Throwable throwable) {
            loadBalancerManager.submitAnomalyServer(groupName, server);
            return this.loadBalancerRequest(requestBase, groupName, proceedPoint);
        }
    }

}